在沒有使用的表單管理套件的情況下,需要定義許多狀態來處理表單邏輯和各種錯誤狀況。而使用像 React Hook Form 這類的套件,可以減少程式碼量,並提供更多功能,使表單處理更加簡單快速。
而使用 Zod 是因為我們希望用戶在表單填入的資料是符合預期的,用 Zod 可以更簡單的定義。
在開始之前,需要先安裝以下套件:
範例程式碼
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
const userSchema = z.object({
  name: z.string(),
  email: z.string().email({ message: "Email is required" }),
  age: z
    .number({
      required_error: "Age is required",
      invalid_type_error: "Age must be a number",
    })
    .int({ message: "Age must be an integer" })
    .refine((age) => age >= 18 && age <= 60, {
      message: "Age must be between 18 and 60",
    }),
  description: z
    .string()
    .max(100, { message: "Description is too long" })
    .optional(),
});
type User = z.infer<typeof userSchema>;
export default function Form() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<User>({
    resolver: zodResolver(userSchema),
    defaultValues: {
      name: "Unknown User",
    },
  });
  const onSubmit = (values: User) => {
    //只有型別驗證通過,才會執行
    console.log(values);
    /*
      {
        "name": "John",
        "email": "test@test.com",
        "age": 25,
      }
      */
    // ... 處理表單邏輯
  };
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="name">Name : </label>
      <input id="name" type="text" {...register("name")} />
      {errors.name && <p>{errors.name.message}</p>}
      <label htmlFor="email">Email : </label>
      <input id="email" type="email" {...register("email")} />
      {errors.email && <p>{errors.email.message}</p>}
      <label htmlFor="age">Age : </label>
      <input
        id="age"
        type="number"
        {...register("age", { valueAsNumber: true })}
      />
      {errors.age && <p>{errors.age.message}</p>}
      <label htmlFor="description">Description : </label>
      <textarea id="description" type="text" {...register("description")} />
      {errors.description && <p>{errors.description.message}</p>}
      <button disabled={isSubmitting} type="submit">
        {isSubmitting ? "Submitting..." : "Submit"}
      </button>
    </form>
  );
}
Zod 除了一般常見的型別外,也可以針對一些特殊的型別做驗證,像是 email 格式或其他自訂的驗證規則。
以下是範例的欄位說明:
refine 來自訂驗證規則,確保年齡必須是整數且介於 18 到 60 歲之間。驗證規則都可以附加 message 參數,用來定義自訂的錯誤訊息。required_error 和 invalid_type_error 參數則是用來定義必填欄位和無效型別的錯誤訊息。
針對非必填欄位,也可以使用 optional 來設定是否必填。
register:透過 register 方法來連接 input 欄位與 React Hook Form,使得它能夠追蹤這些欄位的值與錯誤狀態。handleSubmit:當表單提交時,會根據定義的驗證規則來判斷表單是否有效,並接收傳入的表單資料。formState: { errors, isSubmitting }:這邊包含表單的各種狀態,errors 用來取得各個欄位的錯誤訊息,isSubmitting 則是用來表示表單是否正在提交中。resolver: zodResolver(userSchema):將 Zod 的 schema 傳入作為表單的驗證邏輯。defaultValues: 用來設定表單初始值,例如範例中的 name 預設為 "Unknown User"。handleSubmit(onSubmit): 若表單通過驗證,才會執行 onSubmit 函式。{...register("欄位名稱")}:透過 register 方法將 input 欄位與 React Hook Form 連接,實現對欄位值和錯誤狀態的追蹤。valueAsNumber:會在型別驗證前先把 string 轉成 number。{errors.age && <p>{errors.age.message}</p>} 來動態顯示相應的錯誤資訊。其他更多的說明可以參考 React Hook Form 和 Zod 的文件。